The goal here is to create a prediction model for the value of benthic over the Chesepeake bay region.
This prediction model will be graphically portrayed with a:
- Heatmap
- with base plot
- wigh
ggplot2 over an actual map
- Contour plot
- with base plot
- With
ggplot2 over an actual map
- 3d Surface plot
- with base plot
- with a dynamic
plotly surface plot
Preamble
Import Library
The following code will automatically install packages that are not already installed and load them into the library:
if(require('pacman')){
library('pacman')
}else{
install.packages('pacman')
library('pacman')
}
pacman::p_load(sp, EnvStats, gstat, ggplot2, rmarkdown, reshape2, ggmap, RColorBrewer, parallel, dplyr, plotly)
Create Assignments
Note that Benthic.df is included in the EnvStats package and is not a data frame but an sp class, which is very similar to a data frame
#Assignments
benthic <- Benthic.df
lat <- benthic$Latitude
lon <- benthic$Longitude
index <- benthic$Index
Inspect the Data Set
head(benthic, 3)
Lattitude and Longitude
- Lattitude is a line that runs east to west that measures how far north of the equatour a location is
- Lattitude is assigned to the y-axis
- Longitude is a line that runs north to south that measures east/west
- Longitude is assigned to the x-axis
Assign the Co-ordinates
The Benthic Data set is an sp class objects and requires for its co-ordinates to be set:
coordinates(benthic) = ~Longitude + Latitude
Preliminary Step: Create a krige prediction Model
In order to create a krige prediction model two things are required:
- Fitted Variogram Model (e.g. an exponential model)
- A domain values over which to predict values
Create a variogram Model
Create a Variogram
If a 4th degree polynomial model was used to model the benthic values over space, a variogram can be made from that model thusly:
(The quadratic model is a required parameter of the kriging process, in exercise 12.2 a quadratic model will be used, the spacific mathematics of this though is somewhat beyond me)
x <- lon
y <- lat
benthic.vg <- variogram(object = index ~
x + y +
x^2 + x*y + y^2 +
x^3 + x^2*y + x*y^2 + y^3 +
x^4 + x^3*y + x^2*y^2 + x*y^3 + y^4,
data = benthic)
head(benthic.vg)
np dist gamma dir.hor dir.ver id
1 4545 0.02410565 0.8506164 0 0 var1
2 4149 0.06703996 0.9393502 0 0 var1
3 3883 0.11396396 1.1513831 0 0 var1
4 4023 0.16138943 1.4599238 0 0 var1
5 5963 0.20615519 1.5641015 0 0 var1
6 6732 0.24983031 1.8992994 0 0 var1
Fit an exponential model to that variogram
benthic.vg.fit.exp <- fit.variogram(benthic.vg, model=vgm(1,"Exp", 0.5,1))
Thus the variogram model needed for the krige prediction has been ascertained
Create a Grid of Domain values to use as predictor values
Create a sequence of incrementally increasing for the X and Y axis
This will essentially act as the resolution of the overlay, Most monitors are about 140 ppi, hence assuming the plot will be a 9 inch square, about 800 x 800 pixels should look right, but 200 x 200 will be the used due to performance.
xgrid <- seq(min(lon), max(lon), length.out = 200)
ygrid <- seq(min(lat), max(lat), length.out = 200)
Combine into a domain base
This domain base however has to be a spatial object, this can be acheived by defining co-ordinates in the data frame
xy.surface <- expand.grid(lon = xgrid, lat = ygrid)
coordinates(xy.surface) = ~lon + lat
head(xy.surface, 3)
class : SpatialPoints
features : 3
extent : -77.3157, -75.9, 38.0018, 39.4883 (xmin, xmax, ymin, ymax)
coord. ref. : NA
Create the Krige prediction object
benthic.pred <- krige(formula = Index ~1,
locations = benthic,
newdata = xy.surface,
model= benthic.vg.fit.exp)
[using ordinary kriging]
head(benthic.pred)
class : SpatialPointsDataFrame
features : 6
extent : -77.3157, -77.28013, 38.0018, 38.0018 (xmin, xmax, ymin, ymax)
coord. ref. : NA
variables : 2
names : var1.pred, var1.var
min values : 3.12118704456262, 2.29623742979447
max values : 3.14485115010402, 2.32162957155164
Now there is a prediction model, if that prediction model is extrapolated over our domain (xy.surface) the various plots can be created
Part 1: Create a heatmap
In order to create a heatmap, it is first necessary to take the sp prediction object and make it into pixel data, otherwise the heatmap won’t look quite right:
class(benthic.pred)
[1] "SpatialPointsDataFrame"
attr(,"package")
[1] "sp"
gridded(benthic.pred) = TRUE
class(benthic.pred)
[1] "SpatialPixelsDataFrame"
attr(,"package")
[1] "sp"
spplot(benthic.pred["var1.pred"],main="Predictions of Benthic Index - ordinary kriging")

Create it in ggplot2
First it is necessary to create a data frame from the sp object:
benthic.pred.df <- cbind(benthic.pred@coords, benthic.pred@data)
benthic.pred.df.tidy <- melt(benthic.pred.df)
No id variables; using all as measure variables
head(benthic.pred.df)
ggplot() +
geom_tile(data = benthic.pred.df, aes(lon, lat, fill = var1.pred)) +
labs(fill = "Benthic Index") #

This plot would be easier to read if it had discrete intervals rather than a continuous scale:
benthic.pred.df$bin <- cut(benthic.pred.df$var1.pred,
breaks = as.vector(seq(from = 0, to = 5, length.out = 20)))
ggplot() +
geom_tile(data = benthic.pred.df, aes(lon, lat, fill = bin)) +
labs(fill = "Benthic Index",
title = "Heatmap of Benthic Index Values",
x = "Longitude",
y = "Lattitude")

NA
Map overlay
Now by adjusting the colour scale and opacity, this can be used as an overlay for the map from exercise 10.1:

Part 2: Contour Map
In order to view the surface plot, contours can also be used.
Recall from Week 11/Exercise 10, that contour and surface plots work a little differently to ordinary plots.
A domain grid of equally spaced \(x\) and \(y\) values is required and a matrix of \(z\) values that can be overlayed onto that domain grid in order to provide the surface values required to create the surface plot.
The method from Lecture 10/Wk. 11 involved predicting over the square domain of xy.surface, this won’t work here because instead of the predict function, the krige function was used, hence it is necessary to convert the output from the krige function, into the output that would have been given by the predict function, which is the required \(z\) surface matrix.
This can be acheived with the data.matrix function:
z_surface_matrix <- data.matrix(benthic.pred[1])
Base Package
Using base R packages, a contour plot can be created with the contour function:
contour(xgrid, ygrid, z_surface_matrix,
xlab="-Longitude (degrees West)", ylab="Latitude (degrees North)",
main = "contour plot of Krige Prediction",
labcex = 0.8)
mtext("Krige using a 4th degree polynomial model", cex = 0.5)

Using ggplot2
In order to create a contour plot in ggplot 2, it will be necessary to use ‘tidy’ data, which is a table of data such that each column corresponds to a variable:
rownames(z_surface_matrix) <- xgrid
colnames(z_surface_matrix) <- ygrid
benthic.pred.melt <- melt(z_surface_matrix,
varnames = c("Longitude", "Latitude"),
value.name = "Benthic_Index")
head(benthic.pred.melt)
Observe that this is another way of getting the data frame that was required in part 1 (benthic.pred.df), it is included here as another method and for the sake of completion, also it matches the method used in week 10/lecture 11.
Now using ggplot2 a contour plot, can be created:
ggplot(data = benthic.pred.melt, aes(x = Longitude,
y = Latitude,
z = Benthic_Index,
colour = ..level..),
size = 0.5) +
labs(x = "Longitude", y = 'Latitude',
title = 'Benthic Index',
subtitle = "Chesapeake Bay, Maryland",
caption = "Higher index value is better",
col = 'Benthic Index', size = 'Benthic Index') +
geom_contour(lwd = 1) +
theme_classic()

Map Overlay
The advantage to using ggplot2, is the map overlay can be taken advantage of:

Map Overlay with Heatmap
This can be combined with the heatmap layer:
ggmap(ggmap = map) +
labs(fill = "Benthic Index",
x = "Longitude",
y = "Lattitude",
title = "Heatmap of Benthic Index") +
guides(alpha=FALSE) +
geom_tile(data = benthic.pred.df, aes(lon, lat, fill = bin, alpha = var1.pred)) +
geom_contour(data = benthic.pred.melt, aes(x = Longitude,
y = Latitude,
z = Benthic_Index),
col = 'indianred',
binwidth = 0.2,
size = 0.6) +
scale_fill_manual(values = cols)

Part 3: Surface Plots
A surface plot allows a three dimensional view of the model
Base
In base package, a surface plot can be made:
persp(xgrid, ygrid, z_surface_matrix,
xlim = c(-77.3, -75.9), #the values domain needs to be adjusted above
ylim = c(38.1, 39.5), #Make sure to set an appropriate domain
# zlim = c(0, 6),
theta = -45, phi = 30, d = 0.5,
xlab="-Longitude (degrees West)",
ylab="Latitude (degrees North)",
zlab="Benthic Index", ticktype = "detailed") %>% invisible()
surface extends beyond the box
title(main=paste("Surface Plot of Benthic Index", "Loess Smoothing", sep="\n"))

Plotly
Plotly has the advantage of being fully interactive and not as difficult to constrain within a plot/box area:
p <- plot_ly(x = ygrid, y = xgrid, z = z_surface_matrix) %>%
add_surface() %>%
layout(
title = "Benthic Index in Chesapeake Bay",
scene = list(
xaxis = list(title = "Longitude",
range = c(38.2, 38.9)), #You shouldn't need to edit the
yaxis = list(title = "Latitude", #The domain here, do it above
range = c(-77, -75.9)),
zaxis = list(title = "Benthic Index")
))
p
The surface plot really reinforces the fact that the bay has the lowest (i.e. best) benthic index value.
LS0tDQp0aXRsZTogIkV4ZXJjaXNlIDEyLjEiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICB0b2M6IHllcw0KLS0tDQpUaGUgZ29hbCBoZXJlIGlzIHRvIGNyZWF0ZSBhIHByZWRpY3Rpb24gbW9kZWwgZm9yIA0KdGhlIHZhbHVlIG9mIGJlbnRoaWMgb3ZlciB0aGUgQ2hlc2VwZWFrZSBiYXkgcmVnaW9uLg0KDQpUaGlzIHByZWRpY3Rpb24gbW9kZWwgd2lsbCBiZSBncmFwaGljYWxseSBwb3J0cmF5ZWQgd2l0aCBhOg0KDQoxLiBIZWF0bWFwDQogICAgaSkgd2l0aCBiYXNlIHBsb3QNCiAgICBpaSkgd2lnaCBgZ2dwbG90MmAgb3ZlciBhbiBhY3R1YWwgbWFwDQoyLiBDb250b3VyIHBsb3QNCiAgICBpKSB3aXRoIGJhc2UgcGxvdA0KICAgIGlpKSBXaXRoIGBnZ3Bsb3QyYCBvdmVyIGFuIGFjdHVhbCBtYXANCjMuIDNkIFN1cmZhY2UgcGxvdA0KICAgIGkpIHdpdGggYmFzZSBwbG90DQogICAgaWkpIHdpdGggYSBkeW5hbWljIGBwbG90bHlgIHN1cmZhY2UgcGxvdA0KDQoNCiNQcmVhbWJsZQ0KIyNJbXBvcnQgTGlicmFyeQ0KDQpUaGUgZm9sbG93aW5nIGNvZGUgd2lsbCBhdXRvbWF0aWNhbGx5IGluc3RhbGwgcGFja2FnZXMgdGhhdCBhcmUgDQpub3QgYWxyZWFkeSBpbnN0YWxsZWQgYW5kIGxvYWQgdGhlbSBpbnRvIHRoZSBsaWJyYXJ5Og0KDQpgYGB7cn0NCmlmKHJlcXVpcmUoJ3BhY21hbicpKXsNCiAgbGlicmFyeSgncGFjbWFuJykNCn1lbHNlew0KICBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KICBsaWJyYXJ5KCdwYWNtYW4nKQ0KfQ0KDQpwYWNtYW46OnBfbG9hZChzcCwgRW52U3RhdHMsIGdzdGF0LCBnZ3Bsb3QyLCBybWFya2Rvd24sIHJlc2hhcGUyLCBnZ21hcCwgUkNvbG9yQnJld2VyLCBwYXJhbGxlbCwgZHBseXIsIHBsb3RseSkNCmBgYA0KDQojI0NyZWF0ZSBBc3NpZ25tZW50cw0KDQpOb3RlIHRoYXQgIEJlbnRoaWMuZGYgaXMgaW5jbHVkZWQgaW4gdGhlIGBFbnZTdGF0c2AgcGFja2FnZSBhbmQgaXMgbm90IGEgDQpkYXRhIGZyYW1lIGJ1dCBhbiBgc3BgIGNsYXNzLCB3aGljaCBpcyB2ZXJ5IHNpbWlsYXIgdG8gYSBkYXRhIGZyYW1lDQoNCmBgYHtyfQ0KI0Fzc2lnbm1lbnRzDQpiZW50aGljIDwtIEJlbnRoaWMuZGYNCmxhdCAgIDwtIGJlbnRoaWMkTGF0aXR1ZGUNCmxvbiAgIDwtIGJlbnRoaWMkTG9uZ2l0dWRlDQppbmRleCA8LSBiZW50aGljJEluZGV4DQpgYGANCg0KI0luc3BlY3QgdGhlIERhdGEgU2V0DQoNCmBgYHtyfQ0KDQpoZWFkKGJlbnRoaWMsIDMpDQpgYGANCg0KI0xhdHRpdHVkZSBhbmQgTG9uZ2l0dWRlDQoqIExhdHRpdHVkZSBpcyBhIGxpbmUgdGhhdCBydW5zIGVhc3QgdG8gd2VzdCB0aGF0IG1lYXN1cmVzIGhvdyBmYXIgDQpub3J0aCBvZiB0aGUgZXF1YXRvdXIgYSBsb2NhdGlvbiBpcw0KICAgICAgKyBMYXR0aXR1ZGUgaXMgYXNzaWduZWQgdG8gdGhlIHktYXhpcw0KKiBMb25naXR1ZGUgaXMgYSBsaW5lIHRoYXQgcnVucyBub3J0aCB0byBzb3V0aCB0aGF0IG1lYXN1cmVzIGVhc3Qvd2VzdA0KICAgICArIExvbmdpdHVkZSBpcyBhc3NpZ25lZCB0byB0aGUgeC1heGlzDQoNCg0KDQojQXNzaWduIHRoZSBDby1vcmRpbmF0ZXMNClRoZSAqQmVudGhpYyogRGF0YSBzZXQgaXMgYW4gYHNwYCBjbGFzcyBvYmplY3RzIGFuZCByZXF1aXJlcyBmb3IgaXRzIGNvLW9yZGluYXRlcyB0byBiZSBzZXQ6DQoNCmBgYHtyfQ0KY29vcmRpbmF0ZXMoYmVudGhpYykgPSB+TG9uZ2l0dWRlICsgTGF0aXR1ZGUNCmBgYA0KI1ByZWxpbWluYXJ5IFN0ZXA6IENyZWF0ZSBhIGtyaWdlIHByZWRpY3Rpb24gTW9kZWwNCkluIG9yZGVyIHRvIGNyZWF0ZSBhIGtyaWdlIHByZWRpY3Rpb24gbW9kZWwgdHdvIHRoaW5ncyBhcmUgcmVxdWlyZWQ6DQoNCiogRml0dGVkIFZhcmlvZ3JhbSBNb2RlbCAoZS5nLiBhbiBleHBvbmVudGlhbCBtb2RlbCkNCiogQSBkb21haW4gdmFsdWVzIG92ZXIgd2hpY2ggdG8gcHJlZGljdCB2YWx1ZXMNCg0KIyNDcmVhdGUgYSB2YXJpb2dyYW0gTW9kZWwNCg0KDQoNCg0KIyMjQ3JlYXRlIGEgVmFyaW9ncmFtIA0KSWYgYSA0dGggZGVncmVlIHBvbHlub21pYWwgbW9kZWwgd2FzIHVzZWQgdG8gbW9kZWwgdGhlIA0KYmVudGhpYyB2YWx1ZXMgb3ZlciBzcGFjZSwgYSB2YXJpb2dyYW0gY2FuIGJlIG1hZGUgZnJvbQ0KdGhhdCBtb2RlbCB0aHVzbHk6DQoNCihUaGUgcXVhZHJhdGljIG1vZGVsIGlzIGEgcmVxdWlyZWQgcGFyYW1ldGVyIG9mIHRoZSBrcmlnaW5nIHByb2Nlc3MsDQppbiBleGVyY2lzZSAxMi4yIGEgcXVhZHJhdGljIG1vZGVsIHdpbGwgYmUgdXNlZCwNCnRoZSBzcGFjaWZpYyBtYXRoZW1hdGljcyBvZiB0aGlzIHRob3VnaCBpcyBzb21ld2hhdCBiZXlvbmQgbWUpDQoNCmBgYHtyfQ0KeCA8LSBsb24NCnkgPC0gbGF0DQoNCmJlbnRoaWMudmcgPC0gdmFyaW9ncmFtKG9iamVjdCA9IGluZGV4IH4gDQogICAgICAgICAgICAgICAgICAgICAgICAgIHggKyB5ICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHheMiArIHgqeSArIHleMiArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4XjMgKyB4XjIqeSArIHgqeV4yICsgeV4zICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF40ICsgeF4zKnkgKyB4XjIqeV4yICsgeCp5XjMgKyB5XjQsDQogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gYmVudGhpYykNCg0KaGVhZChiZW50aGljLnZnKQ0KYGBgDQoNCg0KIyMjRml0IGFuIGV4cG9uZW50aWFsIG1vZGVsIHRvIHRoYXQgdmFyaW9ncmFtDQoNCmBgYHtyfQ0KYmVudGhpYy52Zy5maXQuZXhwIDwtIGZpdC52YXJpb2dyYW0oYmVudGhpYy52ZywgbW9kZWw9dmdtKDEsIkV4cCIsIDAuNSwxKSkNCmBgYA0KDQpUaHVzIHRoZSB2YXJpb2dyYW0gbW9kZWwgbmVlZGVkIGZvciB0aGUga3JpZ2UgcHJlZGljdGlvbiBoYXMgYmVlbiBhc2NlcnRhaW5lZA0KDQojI0NyZWF0ZSBhIEdyaWQgb2YgRG9tYWluIHZhbHVlcyB0byB1c2UgYXMgcHJlZGljdG9yIHZhbHVlcw0KIyMjIENyZWF0ZSBhIHNlcXVlbmNlIG9mIGluY3JlbWVudGFsbHkgaW5jcmVhc2luZyBmb3IgdGhlIFggYW5kIFkgYXhpcw0KVGhpcyB3aWxsIGVzc2VudGlhbGx5IGFjdCBhcyB0aGUgcmVzb2x1dGlvbiBvZiB0aGUgb3ZlcmxheSwNCk1vc3QgbW9uaXRvcnMgYXJlIGFib3V0IDE0MCBwcGksIGhlbmNlIGFzc3VtaW5nIHRoZSBwbG90IHdpbGwgYmUgYQ0KOSBpbmNoIHNxdWFyZSwgYWJvdXQgODAwIHggODAwIHBpeGVscyBzaG91bGQgbG9vayByaWdodCwgYnV0IA0KMjAwIHggMjAwIHdpbGwgYmUgdGhlIHVzZWQgZHVlIHRvIHBlcmZvcm1hbmNlLg0KYGBge3J9DQp4Z3JpZCAgICAgICA8LSAgc2VxKG1pbihsb24pLCBtYXgobG9uKSwgbGVuZ3RoLm91dCA9IDIwMCkNCnlncmlkICAgICAgIDwtICBzZXEobWluKGxhdCksIG1heChsYXQpLCBsZW5ndGgub3V0ID0gMjAwKQ0KYGBgDQoNCg0KIyMjQ29tYmluZSBpbnRvIGEgZG9tYWluIGJhc2UNClRoaXMgZG9tYWluIGJhc2UgaG93ZXZlciBoYXMgdG8gYmUgYSBzcGF0aWFsIG9iamVjdCwgdGhpcyBjYW4gYmUNCmFjaGVpdmVkIGJ5IGRlZmluaW5nIGNvLW9yZGluYXRlcyBpbiB0aGUgZGF0YSBmcmFtZQ0KYGBge3J9DQp4eS5zdXJmYWNlICA8LSBleHBhbmQuZ3JpZChsb24gPSB4Z3JpZCwgbGF0ID0geWdyaWQpDQpjb29yZGluYXRlcyh4eS5zdXJmYWNlKSA9IH5sb24gKyBsYXQNCmhlYWQoeHkuc3VyZmFjZSwgMykNCmBgYA0KDQojI0NyZWF0ZSB0aGUgS3JpZ2UgcHJlZGljdGlvbiBvYmplY3QNCmBgYHtyfQ0KDQpiZW50aGljLnByZWQgPC0ga3JpZ2UoZm9ybXVsYSA9IEluZGV4IH4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25zID0gIGJlbnRoaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0geHkuc3VyZmFjZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsPSBiZW50aGljLnZnLmZpdC5leHApDQpoZWFkKGJlbnRoaWMucHJlZCkNCmBgYA0KDQpOb3cgdGhlcmUgaXMgYSBwcmVkaWN0aW9uIG1vZGVsLCBpZiB0aGF0IHByZWRpY3Rpb24gbW9kZWwgaXMNCmV4dHJhcG9sYXRlZCBvdmVyIG91ciBkb21haW4gKGB4eS5zdXJmYWNlYCkgdGhlIHZhcmlvdXMgcGxvdHMgY2FuIGJlDQpjcmVhdGVkDQoNCiNQYXJ0IDE6IENyZWF0ZSBhIGhlYXRtYXANCg0KSW4gb3JkZXIgdG8gY3JlYXRlIGEgaGVhdG1hcCwgaXQgaXMgZmlyc3QgbmVjZXNzYXJ5IHRvIHRha2UNCnRoZSBgc3BgIHByZWRpY3Rpb24gb2JqZWN0IGFuZCBtYWtlIGl0IGludG8gcGl4ZWwgZGF0YSwNCm90aGVyd2lzZSB0aGUgaGVhdG1hcCB3b24ndCBsb29rIHF1aXRlIHJpZ2h0Og0KDQpgYGB7cn0NCmNsYXNzKGJlbnRoaWMucHJlZCkNCmdyaWRkZWQoYmVudGhpYy5wcmVkKSA9IFRSVUUNCmNsYXNzKGJlbnRoaWMucHJlZCkNCmBgYA0KDQoNCmBgYHtyfQ0Kc3BwbG90KGJlbnRoaWMucHJlZFsidmFyMS5wcmVkIl0sbWFpbj0iUHJlZGljdGlvbnMgb2YgQmVudGhpYyBJbmRleCAtIG9yZGluYXJ5IGtyaWdpbmciKQ0KYGBgDQoNCg0KIyNDcmVhdGUgaXQgaW4gZ2dwbG90Mg0KRmlyc3QgaXQgaXMgbmVjZXNzYXJ5IHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgZnJvbSB0aGUgYHNwYCBvYmplY3Q6DQoNCmBgYHtyfQ0KYmVudGhpYy5wcmVkLmRmICAgICAgPC0gY2JpbmQoYmVudGhpYy5wcmVkQGNvb3JkcywgYmVudGhpYy5wcmVkQGRhdGEpDQpiZW50aGljLnByZWQuZGYudGlkeSA8LSBtZWx0KGJlbnRoaWMucHJlZC5kZikNCmhlYWQoYmVudGhpYy5wcmVkLmRmKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fdGlsZShkYXRhID0gYmVudGhpYy5wcmVkLmRmLCBhZXMobG9uLCBsYXQsIGZpbGwgPSB2YXIxLnByZWQpKSArDQogIGxhYnMoZmlsbCA9ICJCZW50aGljIEluZGV4IikgIyANCmBgYA0KDQoNCg0KVGhpcyBwbG90IHdvdWxkIGJlIGVhc2llciB0byByZWFkIGlmIGl0IGhhZCBkaXNjcmV0ZSBpbnRlcnZhbHMNCnJhdGhlciB0aGFuIGEgY29udGludW91cyBzY2FsZToNCg0KYGBge3J9DQpiZW50aGljLnByZWQuZGYkYmluIDwtIGN1dChiZW50aGljLnByZWQuZGYkdmFyMS5wcmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLnZlY3RvcihzZXEoZnJvbSA9IDAsIHRvID0gNSwgbGVuZ3RoLm91dCA9IDIwKSkpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV90aWxlKGRhdGEgPSBiZW50aGljLnByZWQuZGYsIGFlcyhsb24sIGxhdCwgZmlsbCA9IGJpbikpICsNCiAgbGFicyhmaWxsID0gIkJlbnRoaWMgSW5kZXgiLCANCiAgICAgICB0aXRsZSA9ICJIZWF0bWFwIG9mIEJlbnRoaWMgSW5kZXggVmFsdWVzIiwNCiAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgeSA9ICJMYXR0aXR1ZGUiKQ0KICANCmBgYA0KDQojIyNNYXAgb3ZlcmxheQ0KDQpOb3cgYnkgYWRqdXN0aW5nIHRoZSBjb2xvdXIgc2NhbGUgYW5kIG9wYWNpdHksIHRoaXMgY2FuIGJlIHVzZWQgYXMgYW4gb3ZlcmxheQ0KZm9yIHRoZSBtYXAgZnJvbSBleGVyY2lzZSAxMC4xOg0KDQpgYGB7cn0NCmJlbnRoaWMucHJlZC5kZiRiaW4gPC0gY3V0KGJlbnRoaWMucHJlZC5kZiR2YXIxLnByZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMudmVjdG9yKHNlcShmcm9tID0gMCwgdG8gPSA1LCBsZW5ndGgub3V0ID0gOSkpKQ0KDQojR2V0IGEgbWFwDQogIGJib3ggPC0gbWFrZV9iYm94KGJlbnRoaWMucHJlZC5kZiRsb24sIGJlbnRoaWMucHJlZC5kZiRsYXQsIGYgPSAwLjAxKQ0KICBtYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGJib3gsIG1hcHR5cGUgPSAndG9uZXInKQ0KICAjQ3JlYXRlIHRoZSBjb2xvdXJzDQogIGNvbHMgPC0gYnJld2VyLnBhbChuID0gbGVuZ3RoKGxldmVscyhiZW50aGljLnByZWQuZGYkYmluKSksIG5hbWUgPSAiT3JhbmdlcyIpIA0KICAjQ3JlYXRlIHRoZSBQbG90DQogIGdnbWFwKGdnbWFwID0gbWFwKSArDQogIGdlb21fdGlsZShkYXRhID0gYmVudGhpYy5wcmVkLmRmLCBhZXMobG9uLCBsYXQsIGZpbGwgPSBiaW4sIGFscGhhID0gdmFyMS5wcmVkKSkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsNCiAgbGFicyhmaWxsID0gIkJlbnRoaWMgSW5kZXgiLA0KICAgICAgIHggPSAiTG9uZ2l0dWRlIiwNCiAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgIHRpdGxlID0gIkhlYXRtYXAgb2YgQmVudGhpYyBJbmRleCIpICsgDQogIGd1aWRlcyhhbHBoYT1GQUxTRSkgDQogICAgDQpgYGANCg0KDQojUGFydCAyOiBDb250b3VyIE1hcA0KSW4gb3JkZXIgdG8gdmlldyB0aGUgc3VyZmFjZSBwbG90LCBjb250b3VycyBjYW4gYWxzbyBiZSB1c2VkLg0KDQogUmVjYWxsIGZyb20gV2VlayAxMS9FeGVyY2lzZSAxMCwgdGhhdCBjb250b3VyIGFuZCBzdXJmYWNlIHBsb3RzDQogIHdvcmsgYSBsaXR0bGUgZGlmZmVyZW50bHkgdG8gb3JkaW5hcnkgcGxvdHMuDQogIA0KICBBIGRvbWFpbiBncmlkIG9mIGVxdWFsbHkgc3BhY2VkICR4JCBhbmQgJHkkIHZhbHVlcyBpcyByZXF1aXJlZCBhbmQNCiAgYSBtYXRyaXggb2YgJHokIHZhbHVlcyB0aGF0IGNhbiBiZSBvdmVybGF5ZWQgb250byB0aGF0IGRvbWFpbiBncmlkDQogIGluIG9yZGVyIHRvIHByb3ZpZGUgdGhlIHN1cmZhY2UgdmFsdWVzIHJlcXVpcmVkIA0KICB0byBjcmVhdGUgdGhlIHN1cmZhY2UgcGxvdC4NCg0KDQoNCiAgVGhlIG1ldGhvZCBmcm9tIExlY3R1cmUgMTAvV2suIDExIGludm9sdmVkIHByZWRpY3Rpbmcgb3Zlcg0KICB0aGUgc3F1YXJlIGRvbWFpbiBvZiBgeHkuc3VyZmFjZWAsIHRoaXMgd29uJ3Qgd29yayBoZXJlDQogIGJlY2F1c2UgaW5zdGVhZCBvZiB0aGUgYHByZWRpY3RgIGZ1bmN0aW9uLCB0aGUgYGtyaWdlYCBmdW5jdGlvbg0KICB3YXMgdXNlZCwgaGVuY2UgaXQgaXMgbmVjZXNzYXJ5IHRvIGNvbnZlcnQgdGhlIG91dHB1dCBmcm9tDQogIHRoZSBga3JpZ2VgIGZ1bmN0aW9uLCBpbnRvIHRoZSBvdXRwdXQgdGhhdCB3b3VsZCBoYXZlIGJlZW4gDQogIGdpdmVuIGJ5IHRoZSBwcmVkaWN0IGZ1bmN0aW9uLCB3aGljaCBpcyB0aGUgcmVxdWlyZWQNCiAgJHokIHN1cmZhY2UgbWF0cml4Lg0KICANCiAgVGhpcyBjYW4gYmUgYWNoZWl2ZWQgd2l0aCB0aGUgYGRhdGEubWF0cml4YCBmdW5jdGlvbjoNCiAgDQogIA0KYGBge3J9DQp6X3N1cmZhY2VfbWF0cml4IDwtIGRhdGEubWF0cml4KGJlbnRoaWMucHJlZFsxXSkNCmBgYA0KDQojI0Jhc2UgUGFja2FnZQ0KVXNpbmcgYmFzZSBSIHBhY2thZ2VzLCBhIGNvbnRvdXIgcGxvdCBjYW4gYmUgY3JlYXRlZCB3aXRoIHRoZSBgY29udG91cmANCmZ1bmN0aW9uOg0KYGBge3J9DQpjb250b3VyKHhncmlkLCB5Z3JpZCwgel9zdXJmYWNlX21hdHJpeCwNCiAgICAgICAgICB4bGFiPSItTG9uZ2l0dWRlIChkZWdyZWVzIFdlc3QpIiwgeWxhYj0iTGF0aXR1ZGUgKGRlZ3JlZXMgTm9ydGgpIiwNCiAgICAgICAgICBtYWluID0gImNvbnRvdXIgcGxvdCBvZiBLcmlnZSBQcmVkaWN0aW9uIiwNCiAgICAgICAgICBsYWJjZXggPSAwLjgpDQogIG10ZXh0KCJLcmlnZSB1c2luZyBhIDR0aCBkZWdyZWUgcG9seW5vbWlhbCBtb2RlbCIsIGNleCA9IDAuNSkNCmBgYA0KDQojI1VzaW5nIGdncGxvdDINCkluIG9yZGVyIHRvIGNyZWF0ZSBhIGNvbnRvdXIgcGxvdCBpbiBnZ3Bsb3QgMiwNCml0IHdpbGwgYmUgbmVjZXNzYXJ5IHRvIHVzZSAndGlkeScgZGF0YSwgd2hpY2ggaXMgDQphIHRhYmxlIG9mIGRhdGEgc3VjaCB0aGF0IGVhY2ggY29sdW1uIGNvcnJlc3BvbmRzDQp0byBhIHZhcmlhYmxlOg0KYGBge3J9DQpyb3duYW1lcyh6X3N1cmZhY2VfbWF0cml4KSA8LSB4Z3JpZA0KICBjb2xuYW1lcyh6X3N1cmZhY2VfbWF0cml4KSA8LSB5Z3JpZA0KICBiZW50aGljLnByZWQubWVsdCA8LSBtZWx0KHpfc3VyZmFjZV9tYXRyaXgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFybmFtZXMgPSBjKCJMb25naXR1ZGUiLCAiTGF0aXR1ZGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIkJlbnRoaWNfSW5kZXgiKQ0KICBoZWFkKGJlbnRoaWMucHJlZC5tZWx0KQ0KYGBgDQogIE9ic2VydmUgdGhhdCB0aGlzIGlzIGFub3RoZXIgd2F5IG9mIGdldHRpbmcgdGhlDQogIGRhdGEgZnJhbWUgdGhhdCB3YXMgcmVxdWlyZWQgaW4gcGFydCAxIChgYmVudGhpYy5wcmVkLmRmYCksDQogIGl0IGlzIGluY2x1ZGVkIGhlcmUgYXMgYW5vdGhlciBtZXRob2QgYW5kIGZvciB0aGUgc2FrZSBvZiBjb21wbGV0aW9uLA0KICBhbHNvIGl0IG1hdGNoZXMgdGhlIG1ldGhvZCB1c2VkIGluIHdlZWsgMTAvbGVjdHVyZSAxMS4NCg0KDQpOb3cgdXNpbmcgZ2dwbG90MiBhIGNvbnRvdXIgcGxvdCwgY2FuIGJlIGNyZWF0ZWQ6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBiZW50aGljLnByZWQubWVsdCwgYWVzKHggPSBMb25naXR1ZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gTGF0aXR1ZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IC4ubGV2ZWwuLiksDQogICAgICAgICBzaXplID0gMC41KSArDQogICAgbGFicyh4ID0gIkxvbmdpdHVkZSIsIHkgPSAnTGF0aXR1ZGUnLA0KICAgICAgICAgdGl0bGUgPSAnQmVudGhpYyBJbmRleCcsDQogICAgICAgICBzdWJ0aXRsZSA9ICJDaGVzYXBlYWtlIEJheSwgTWFyeWxhbmQiLA0KICAgICAgICAgY2FwdGlvbiA9ICJIaWdoZXIgaW5kZXggdmFsdWUgaXMgYmV0dGVyIiwNCiAgICAgICAgIGNvbCA9ICdCZW50aGljIEluZGV4Jywgc2l6ZSA9ICdCZW50aGljIEluZGV4JykgKyANCiAgICBnZW9tX2NvbnRvdXIobHdkID0gMSkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyNNYXAgT3ZlcmxheQ0KVGhlIGFkdmFudGFnZSB0byB1c2luZyBnZ3Bsb3QyLCBpcyB0aGUgbWFwIG92ZXJsYXkgY2FuIGJlIHRha2VuIGFkdmFudGFnZSBvZjoNCg0KDQpgYGB7cn0NCiAgbWFwIDwtIGdldF9tYXAobG9jYXRpb24gPSBiYm94LCBtYXB0eXBlID0gJ3dhdGVyY29sb3InKQ0KICANCiAgZ2dtYXAoZ2dtYXAgPSBtYXApICsNCiAgICBsYWJzKGZpbGwgPSAiQmVudGhpYyBJbmRleCIsDQogICAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgICAgdGl0bGUgPSAiSGVhdG1hcCBvZiBCZW50aGljIEluZGV4IikgKyANCiAgICBndWlkZXMoYWxwaGE9RkFMU0UpICsNCiAgICBnZW9tX2NvbnRvdXIoZGF0YSA9IGJlbnRoaWMucHJlZC5tZWx0LCBhZXMoeCA9IExvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCksDQogICAgICAgICAgICAgICAgIGNvbCA9ICdncmV5NDAnLA0KICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMTUsDQogICAgICAgICAgICAgICAgIHNpemUgPSAwLjcwKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykgDQpgYGANCg0KIyMjTWFwIE92ZXJsYXkgd2l0aCBIZWF0bWFwDQpUaGlzIGNhbiBiZSBjb21iaW5lZCB3aXRoIHRoZSBoZWF0bWFwIGxheWVyOg0KDQpgYGB7cn0NCiAgZ2dtYXAoZ2dtYXAgPSBtYXApICsNCiAgICBsYWJzKGZpbGwgPSAiQmVudGhpYyBJbmRleCIsDQogICAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgICAgdGl0bGUgPSAiSGVhdG1hcCBvZiBCZW50aGljIEluZGV4IikgKyANCiAgICBndWlkZXMoYWxwaGE9RkFMU0UpICsNCiAgICBnZW9tX3RpbGUoZGF0YSA9IGJlbnRoaWMucHJlZC5kZiwgYWVzKGxvbiwgbGF0LCBmaWxsID0gYmluLCBhbHBoYSA9IHZhcjEucHJlZCkpICsNCiAgICBnZW9tX2NvbnRvdXIoZGF0YSA9IGJlbnRoaWMucHJlZC5tZWx0LCBhZXMoeCA9IExvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCksDQogICAgICAgICAgICAgICAgY29sID0gJ2luZGlhbnJlZCcsDQogICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjIsDQogICAgICAgICAgICAgICAgc2l6ZSA9IDAuNikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpIA0KYGBgDQoNCiNQYXJ0IDM6IFN1cmZhY2UgUGxvdHMNCkEgc3VyZmFjZSBwbG90IGFsbG93cyBhIHRocmVlIGRpbWVuc2lvbmFsIHZpZXcgb2YgdGhlIG1vZGVsDQoNCiMjQmFzZQ0KSW4gYmFzZSBwYWNrYWdlLCBhIHN1cmZhY2UgcGxvdCBjYW4gYmUgbWFkZToNCmBgYHtyfQ0KcGVyc3AoeGdyaWQsIHlncmlkLCB6X3N1cmZhY2VfbWF0cml4LA0KICAgICAgICB4bGltID0gYygtNzcuMywgLTc1LjkpLCAgICN0aGUgdmFsdWVzIGRvbWFpbiBuZWVkcyB0byBiZSBhZGp1c3RlZCBhYm92ZQ0KICAgICAgICB5bGltID0gYygzOC4xLCAzOS41KSwgICAgICAgICAgI01ha2Ugc3VyZSB0byBzZXQgYW4gYXBwcm9wcmlhdGUgZG9tYWluDQogICAgICAgICMgemxpbSA9IGMoMCwgNiksDQogICAgICAgIHRoZXRhID0gLTQ1LCBwaGkgPSAzMCwgZCA9IDAuNSwNCiAgICAgICAgeGxhYj0iLUxvbmdpdHVkZSAoZGVncmVlcyBXZXN0KSIsDQogICAgICAgIHlsYWI9IkxhdGl0dWRlIChkZWdyZWVzIE5vcnRoKSIsDQogICAgICAgIHpsYWI9IkJlbnRoaWMgSW5kZXgiLCB0aWNrdHlwZSA9ICJkZXRhaWxlZCIpDQogIHRpdGxlKG1haW49cGFzdGUoIlN1cmZhY2UgUGxvdCBvZiBCZW50aGljIEluZGV4IiwgIkxvZXNzIFNtb290aGluZyIsIHNlcD0iXG4iKSkgICANCmBgYA0KDQojI1Bsb3RseQ0KUGxvdGx5IGhhcyB0aGUgYWR2YW50YWdlIG9mIGJlaW5nIGZ1bGx5IGludGVyYWN0aXZlIGFuZCBub3QgYXMgZGlmZmljdWx0IHRvIA0KY29uc3RyYWluIHdpdGhpbiBhIHBsb3QvYm94IGFyZWE6DQpgYGB7cn0NCnAgPC0gcGxvdF9seSh4ID0geWdyaWQsIHkgPSB4Z3JpZCwgeiA9IHpfc3VyZmFjZV9tYXRyaXgpICU+JQ0KICAgIGFkZF9zdXJmYWNlKCkgJT4lDQogICAgbGF5b3V0KA0KICAgICAgdGl0bGUgPSAiQmVudGhpYyBJbmRleCBpbiAgQ2hlc2FwZWFrZSBCYXkiLA0KICAgICAgc2NlbmUgPSBsaXN0KA0KICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiTG9uZ2l0dWRlIiwNCiAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygzOC4yLCAzOC45KSksICAjWW91IHNob3VsZG4ndCBuZWVkIHRvIGVkaXQgdGhlDQogICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJMYXRpdHVkZSIsICAgICAgI1RoZSBkb21haW4gaGVyZSwgZG8gaXQgYWJvdmUNCiAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygtNzcsIC03NS45KSksDQogICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJCZW50aGljIEluZGV4IikNCiAgICAgICkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgcA0KYGBgDQoNCg0KDQpUaGUgc3VyZmFjZSBwbG90IHJlYWxseSByZWluZm9yY2VzIHRoZSBmYWN0IHRoYXQgdGhlIGJheSBoYXMNCnRoZSBsb3dlc3QgKGkuZS4gYmVzdCkgYmVudGhpYyBpbmRleCB2YWx1ZS4NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=